/******************************************************************************
* echo_server.c                                                               *
*                                                                             *
* Description: This file contains the C source code for an echo server.  The  *
*              server runs on a hard-coded port and simply write back anything*
*              sent to it by connected clients.  It does not support          *
*              concurrent clients.                                            *
*                                                                             *
* Authors: Athula Balachandran <abalacha@cs.cmu.edu>,                         *
*          Wolf Richter <wolf@cs.cmu.edu>                                     *
*                                                                             *
*******************************************************************************/
#include "parse.h"
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>

#define ECHO_PORT 9999
#define BUF_SIZE 4096


const char* bad400 = "HTTP/1.1 400 Bad request\r\n\r\n";
const char* not501 = "HTTP/1.1 501 Not Implemented\r\n\r\n";
const char* ok200 = "HTTP/1.1 200 OK\r\n";
const char* not404 = "HTTP/1.1 404 Not Found\r\n\r\n";
const char* not505 = "HTTP/1.1 505 HTTP Version not supported\r\n\r\n";


int write_log(char *message){
    FILE *f = fopen("./log.txt", "at+");
    if(f == NULL){
        fprintf(stderr, "Failed writing log file.\n");
    }
    time_t t = time(NULL);
    struct tm *local_time = localtime(&t);
    fprintf(f, "Local time: %d-%d-%d %d:%d:%d\n", 
        local_time->tm_year + 1900, 
        local_time->tm_mon + 1, 
        local_time->tm_mday, 
        local_time->tm_hour, 
        local_time->tm_min, 
        local_time->tm_sec);
    fprintf(f, "%s \r\n\r\n", message);
    fclose(f);
    return 1;
}


const char *get_content_type(const char *file_name) {
    const char *ext = strrchr(file_name, '.'); // 找到最后一个点（.），获取文件扩展名
    if (ext == NULL) {
        return "application/octet-stream";  // 如果没有扩展名，则默认返回通用的二进制类型
    }
    ext++;  // 跳过点（.）
    if (strcasecmp(ext, "html") == 0) {
        return "text/html";
    } else if (strcasecmp(ext, "css") == 0) {
        return "text/css";
    } else if (strcasecmp(ext, "js") == 0) {
        return "application/javascript";
    } else if (strcasecmp(ext, "json") == 0) {
        return "application/json";
    } else if (strcasecmp(ext, "png") == 0) {
        return "image/png";
    } else if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0) {
        return "image/jpeg";
    } else if (strcasecmp(ext, "gif") == 0) {
        return "image/gif";
    } else if (strcasecmp(ext, "txt") == 0) {
        return "text/plain";
    } else if (strcasecmp(ext, "xml") == 0) {
        return "application/xml";
    } else if (strcasecmp(ext, "pdf") == 0) {
        return "application/pdf";
    } else {
        return "application/octet-stream"; // 默认二进制流文件
    }
};


int close_socket(int sock)
{
    if (close(sock))
    {
        fprintf(stderr, "Failed closing socket.\n");
        return 1;
    }
    return 0;
}

int send_response(int client_sock, const char *response, const char *body, size_t body_size){
    char* print = (char*)malloc(body_size + strlen(response));
    int result = (strlen(response) + body_size);
    sprintf(print, "%s%s", response, body);
    write_log(print);
    send(client_sock, print, result, 0);
    free(print);
    return result; 
};

void send_file(int client_sock, const char *path, int ishead) {
    FILE* sdfile = fopen(path, "rb");
    if (sdfile == NULL) {
        write_log("Failed finding file.");
        send_response(client_sock, not404, "", strlen(not404));
        fprintf(stderr, "Failed finding file.\n"); 
        return;
    }
    char* content_type = get_content_type(path);
    struct stat file_stat;
    if (stat(path, &file_stat) != 0) {
        write_log(not404);
        send_response(client_sock, not404, "", strlen(not404));
        fclose(sdfile);
        fprintf(stderr, "Failed to stat file: %s\n", path);
        return;
    }
    char header[512];
    snprintf(header, sizeof(header),
        "%s"
        "Content-Type: %s\r\n"
        "Content-Length: %ld\r\n"
        "Connection: keep-alive\r\n\r\n",
        ok200, content_type,file_stat.st_size);
    if(ishead == 1){
    send(client_sock, header, strlen(header), 0);
    fclose(sdfile);  // 就发送头部
    return;
    }
    send(client_sock, header, strlen(header), 0);
    //执行GET剩余部分
    ssize_t bytes_read;
    int bytes_sent = 0;
    // bytes_read = fread(file_buff, BUF_SIZE, file_stat.st_size, sdfile);
    // send(client_sock, file_buff, bytes_read, 0);
    char file_buff[BUF_SIZE];
    while ((bytes_read = fread(file_buff, 1, BUF_SIZE, sdfile)) > 0) {
    bytes_sent = send(client_sock, file_buff, bytes_read, 0);
    if (bytes_sent == -1) {
        fprintf(stderr, "Response Failed!");
        fclose(sdfile);
        return;
    }
    }
    fclose(sdfile);
    return ;
    
};

int main(int argc, char* argv[])
{
    int sock, client_sock;
    ssize_t readret;
    socklen_t cli_size;
    struct sockaddr_in addr, cli_addr;
    char recv_buf[BUF_SIZE];    // 储存每次recv()的数据
    char pipeline_buf[2 * BUF_SIZE];  //未处理的接收数据，从recv_buf[]循环追加

    fprintf(stdout, "----- Liso Server -----\n");
    
    /* all networked programs must create a socket */
    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {   
        write_log("Failed creating socket");
        fprintf(stderr, "Failed creating socket.\n");
        return EXIT_FAILURE;
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons(ECHO_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;
    /* servers bind sockets to ports---notify the OS they accept connections */
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt) );
    
    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)))
    {
        //associate that socket with a port on host
        close_socket(sock);
        write_log("Failed binding socket");
        fprintf(stderr, "Failed binding socket.\n");
        return EXIT_FAILURE;
    }
    
    if (listen(sock, 5))
    {
        close_socket(sock);
        write_log("Error listening on socket.");
        fprintf(stderr, "Error listening on socket.\n");
        return EXIT_FAILURE;
    }

    /* finally, loop waiting for input and then write it back */
    while (1)
    {
        fprintf(stdout, "----- Waiting for connection -----\n");
        cli_size = sizeof(cli_addr);
        if ((client_sock = accept(sock, (struct sockaddr *) &cli_addr, &cli_size)) == -1)
        {
            close(sock);
            fprintf(stderr, "Error accepting connection.\n");
            return EXIT_FAILURE;
        }

        // 设置接收超时时间为5秒
        struct timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        if (setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
            perror("setsockopt failed");
            close(client_sock);
            close(sock);
            return EXIT_FAILURE;
        }
        // 接收准备工作
        int client_on = 1;
        int keep_alive = 1;
        int count_request = 0;
        memset(recv_buf, 0, BUF_SIZE);   // 初始化缓冲区 
        memset(pipeline_buf, 0, 2 * BUF_SIZE);

        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(cli_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
        fprintf(stdout, "----- Connected to: %s------\n", client_ip);

        while(client_on && keep_alive)
        {
            readret = 0;
            memset(recv_buf, 0, BUF_SIZE);   // 初始化缓冲区 
            fprintf(stdout, "Waiting for next message..........\n");
            readret = recv(client_sock, recv_buf, BUF_SIZE, 0);
            if (readret <= 0)
            {
                if (errno == EWOULDBLOCK || errno == EAGAIN) {
                    printf("recv timeout\n");
                    client_on = 0;
                    break;
                } else {
                    perror("recv failed");
                    close_socket(client_sock);
                    close_socket(sock);
                    return EXIT_FAILURE;
                }
                
            }
            // fprintf(stdout, "pipeline_buf(50) :\n%.50s\n",pipeline_buf);       //调试用！！！！！！
            // 将 recv_buf 的内容追加到 pipeline_buf 中
            if (strlen(pipeline_buf) + readret < sizeof(pipeline_buf))
            {
                strncat(pipeline_buf, recv_buf, readret);
            }
            else
            {
                fprintf(stderr, "Not enough space in pipeline_buf to append recv_buf.\n");
                client_on = 0;
                continue;
            }
            // fprintf(stdout, "pipeline_buf :\n%.200s\n",pipeline_buf);       //调试用！！！！！！
            char* cur_request = pipeline_buf; // 当前request起始位置指针
            char* next_request = pipeline_buf; // 下一个request起始位置指针

            while (keep_alive && client_on && (next_request = strstr(cur_request, "\r\n\r\n")) != NULL) 
            {
                count_request ++;
                // 找到下一个request开头
                next_request += 4;

                // 对当前request进行操作
                Request* request = parse(cur_request, readret);
                
                int backlength;
                if (request == NULL)
                {
                    write_log(bad400);
                    send(client_sock, bad400, strlen(bad400), 0);
                    // client_on = 0;
                    // break;
                }
                else if (strcmp(request -> http_version, "HTTP/1.1") != 0){
                    write_log(not505);
                    send(client_sock, not505, strlen(not505), 0);
                    // client_on = 0;
                    // break;
                }
                else if (strcmp(request -> http_method, "HEAD") == 0)
                {
                    char *path[4096];
                    memset(path, 0 ,BUF_SIZE);
                    if (strcmp(request->http_uri, "/") == 0) {
                        strcat(path,  "./static_site/index.html");  // 默认页面
                    }
                    else {
                        strcat(path, "./static_site/");
                        strcat(path, request->http_uri);
                    }
                    send_file(client_sock, path, 1);
                }
                else if (strcmp(request->http_method, "GET") == 0){
                    char *path[4096];
                    memset(path, 0 ,BUF_SIZE);
                    if (strcmp(request->http_uri, "/") == 0) {
                        strcat(path,  "./static_site/index.html");  // 默认页面
                    }
                    else {
                        strcat(path, "./static_site/");
                        strcat(path, request->http_uri);
                    }
                    send_file(client_sock, path, 0);
                }
                else if (strcmp(request->http_method, "POST") == 0){
                    // Re-encapsulate the received request and send it back
                    write_log("POST 200 OK.");
                    backlength = send_response(client_sock, ok200, recv_buf, readret);
                }
                else
                {
                    write_log(not501);
                    send(client_sock, not501, strlen(not501), 0);
                    // client_on = 0;
                    // break;
                }

                // 判断是否keep_alive
                keep_alive = 0; // 默认keep_alive = 0
                if(request != NULL){
                    for (int head_pointer = 0; head_pointer < request->header_count; head_pointer++)
                    {
                        Request_header *curheader = &request->headers[head_pointer];
                        if (strcmp(curheader->header_name, "Connection") == 0)
                        {
                            if(strcmp(curheader->header_value, "keep-alive") == 0){
                                keep_alive = 1;
                            }
                            else if(strcmp(curheader->header_value, "close") == 0)
                            {
                                keep_alive = 0;
                            }
                            else
                            {
                                fprintf(stderr, "invalid connection-value!\n");
                            }
                        }
                    }
                }
                fprintf(stdout, "request%d: %s, keepalive = %d, client_on = %d\n", count_request, request? request->http_method : "NULL", keep_alive, client_on);
                client_on = keep_alive;
                // 移动到下一个request开头
                if (request!= NULL){
                    if(request != NULL){
                        free(request->headers);
                    }
                    free(request);
                }
                cur_request = next_request;
            }
            // 清空 pipeline_buf 内 cur_request 指针前的内容，将后面的内容移到开头
            size_t remaining_data_size = strlen(cur_request);
            memmove(pipeline_buf, cur_request, remaining_data_size);
            memset(pipeline_buf + remaining_data_size, 0, sizeof(pipeline_buf) - remaining_data_size);
            // 进行下一次recv()
        }
        if (close_socket(client_sock))
        {
            fprintf(stderr, "------ Error closing client socket. ------\n");
        }
        client_on = 0;
        fprintf(stdout, "------connection closed------\n");
        // 等待下一次连接
    }

    if (close_socket(client_sock))
            {
                close_socket(sock);
                fprintf(stderr, "Error closing client socket.\n");
                return EXIT_FAILURE;
            }
    close_socket(sock);
    return EXIT_SUCCESS;
}
